﻿using System.Threading;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using XcoAppSpaces.Core.Ports;

namespace test.XcoAppSpaces.Core.Ports
{
	[TestFixture]
	public class ConcurrentExclusiveGroupTests
	{
		private List<string> sequence;

		[Test]
		public void ReaderWriterTest()
		{
			//Arrange
			var writerPort = new Port<int>();
			writerPort.RegisterReceiver((Func<int, Task>)Writer, 1);
			var readerPort = new Port<int>();
			readerPort.RegisterReceiver((Func<int, Task>)Reader, 4);
			sequence = new List<string>();

			//Act
			var group = new ConcurrentExclusiveGroup();
			group.AddExclusivePort(writerPort);
			group.AddConcurrentPort(readerPort);
			writerPort.Post(1);
			writerPort.Post(2);
			readerPort.Post(1);
			readerPort.Post(2);
			readerPort.Post(3);

			//Assert
			Assert.IsTrue(SpinWait.SpinUntil(() => sequence.Count == 10, 3000));
			CheckThatThereIsNothingBetweenWriterStartAndEnd(1);
			CheckThatThereIsNothingBetweenWriterStartAndEnd(2);
		}

		private void CheckThatThereIsNothingBetweenWriterStartAndEnd(int writerId)
		{
			var startIdx = sequence.IndexOf("start writer " + writerId);
			var endIdx = sequence.IndexOf("end writer " + writerId);
			Assert.That(endIdx, Is.EqualTo(startIdx+1));
		}

		private async Task Reader(int num)
		{
			lock (sequence)
				sequence.Add("start reader " + num);
			Console.WriteLine("start reader " + num);
			await Task.Delay(500);
			lock (sequence)
				sequence.Add("end reader " + num);
			Console.WriteLine("end reader " + num);
		}

		private async Task Writer(int num)
		{
			sequence.Add("start writer " + num);
			Console.WriteLine("start writer " + num);
			await Task.Delay(500);
			sequence.Add("end writer " + num);
			Console.WriteLine("end writer " + num);
		}
	}
}
